/*
Abstract:
  Data synthesizing functionality implementation

Last changed:
  $Id: synthesizer.cpp 88 2010-01-15 01:06:37Z vitamin.caig $

Author:
  (C) Vitamin/CAIG/2001

  This file is the part of Textator project
*/
#include "synthesizer.h"
#include "../io/logging.h"

#include <error.h>
#include <format.h>
#include <error_codes.h>

#include <cassert>
#include <iterator>
#include <algorithm>

#include <text.h>

#define FILE_TAG 12B75C3C

namespace
{
  void CheckCycle(const String& id, const std::list<String>& chain)
  {
    if (chain.end() != std::find(chain.begin(), chain.end(), id))
    {
      OStringStream report;
      std::copy(chain.begin(), chain.end(), std::ostream_iterator<String, String::value_type>(report, TEXT_FORMAT_CYCLIC_DELIMITER));
      report << id;
      throw Error(ERROR_DETAIL, ERR_CYCLIC_DEPENDENCY,
        StringFormatter::Process(TEXT_ERROR_CYCLIC_DEPENDENCY, report.str()));
    }
  }

  String Instantiate(const String& id, EntriesMap& map, std::list<String> chain)
  {
    CheckCycle(id, chain);
    EntriesMap::iterator subids(map.find(id));
    if (map.end() == subids)
    {
      throw Error(ERROR_DETAIL, ERR_TOKEN_NOT_FOUND,
        StringFormatter::Process(TEXT_ERROR_TOKEN_NOT_FOUND, id));
    }
    if (1 == subids->second.Value.size() &&
      Token::TEXT == subids->second.Value.front().Type)
    {
      return subids->second.Value.front().Value;
    }

    Log::Debug("<$1> Instantiating token '$2'", chain.size(), id);
    chain.push_back(id);

    String result;
    for (TokensList::const_iterator it = subids->second.Value.begin(), lim = subids->second.Value.end(); it != lim; ++it)
    {
      if (Token::TEXT == it->Type)
      {
        result += it->Value;
      }
      else if (Token::VARIABLE == it->Type)
      {
        result += Instantiate(it->Value, map, chain);
      }
      else
      {
        assert(!"Invalid token type");
      }
    }
    subids->second.Value.assign(1, Token(Token::TEXT, result));
    return result;
  }
}

namespace Synthesizer
{
  void Process(const EntriesMap& map, StringMap& result)
  {
    std::list<String> dummy;
    EntriesMap tmp(map);
    result.clear();
    for (EntriesMap::const_iterator it = tmp.begin(), lim = tmp.end(); it != lim; ++it)
    {
      if (Entry::GLOBAL == it->second.Type)
      {
        result.insert(StringMap::value_type(it->first, Instantiate(it->first, tmp, dummy)));
      }
    }
  }
}
